/*
	weak_type_holer.cpp
	define the data holder for weak type
	this is the functional holder for underlying structure
	@author Wolfhead Mei
*/

#ifndef WOLV_WEAK_TYPE_HOLDER_HPP
#define WOLV_WEAK_TYPE_HOLDER_HPP


#include "weak_type_detail.hpp"
#include <string.h> 

#ifdef WIN32
#define my_stricmp _stricmp
#else
#define my_stricmp strcasecmp
#endif

namespace wolv
{
namespace weakType
{
namespace _detail
{

class weakTypeHolder
{
public:
	weakTypeHolder() :
		type_(nullType),
		count_(0),
		ref_(data2ref(_data_))
	{
		SetNull();
	};


	weakTypeHolder(const weakTypeHolder& value) :
		type_(nullType),
		count_(0),
		ref_(data2ref(_data_))
	{
		type_ = value.type_;
		__allocDataHolder(type_, ref_, value.ref_);
		SetNull();
	};

	


	virtual ~weakTypeHolder()
	{
		__destroyDataHolder(type_, ref_);
	};


	void Ref() {++count_;}; 
	void Release() {--count_;};
	int Count() {return count_;};
	bool Shared() {return count_ <= 1;}
public:

	valueType GetType()
	{
		return type_;
	}

	void SetType(valueType type)
	{
		if (type_ == type) return;
		__destroyDataHolder(type_, ref_);
		type_ = type;
		__allocDataHolder(type_, ref_);
	}

	void SetNull()
	{	
		SetType(nullType);
	}

	void SetInt(int value)
	{	
		if (type_ != intType) 
		{
			__destroyDataHolder(type_, ref_);
			type_ = intType;
			__allocDataHolder(type_, ref_);
		}
		ref_.int_value = value;
		return;
	}

	void SetBool(bool value)
	{	
		if (type_ != boolType) 
		{
			__destroyDataHolder(type_, ref_);
			type_ = boolType;
			__allocDataHolder(type_, ref_);
		}
		ref_.bool_value = value;
		return;
	}

	void SetDouble(double value)
	{	
		if (type_ != doubleType) 
		{
			__destroyDataHolder(type_, ref_);
			type_ = doubleType;
			__allocDataHolder(type_, ref_);
		}
		ref_.double_value = value;
		return;
	}

	void SetString(const std::string& value)
	{	
		if (type_ != stringType) 
		{
			__destroyDataHolder(type_, ref_);
			type_ = stringType;
			__allocDataHolder(type_, ref_);
		}
		ref_.string_value = value;
		return;
	}

	const int & GetInt() {return ref_.int_value;};
	const bool & GetBool() {return ref_.bool_value;};
	const double & GetDouble() {return ref_.double_value;};
	const string_type & GetString() {return ref_.string_value;};
	array_type & GetArray() {return ref_.array_value;};
	object_type & GetObject() {return ref_.object_value;};

	/*
		convert data to int
		default 0
	 */
	int toInt() const
	{
		switch (type_)
		{
		case nullType:
			return 0;
			break;
		case intType:
			return ref_.int_value;
			break;
		case boolType:
			return (int)ref_.bool_value;
			break;
		case doubleType:
			return (int)ref_.double_value;
			break;
		case stringType:
			{
				// trying the best, ignore something like 123abc
				int tmp = 0;
				sscanf(ref_.string_value.c_str(), "%d", &tmp);
				return tmp;
			}
			break;
		case arrayType:
		case objectType:
		default:
			return 0;
		}

		return 0;
	}

	/*
		convert data to bool
		default false
	*/
	bool toBool() const
	{
		switch (type_)
		{
		case nullType:
			return false;
			break;
		case intType:
			if (ref_.int_value > 0)
			{
				return true;
			}
			else
			{
				return false;
			}
			break;
		case boolType:
			return ref_.bool_value;
			break;
		case doubleType:
			if (ref_.double_value > 0)
			{
				return true;
			}
			else
			{
				return false;
			}
			break;
		case stringType:
			if (my_stricmp(ref_.string_value.c_str(), "true") == 0)
			{
				return true;
			}
			else
			{
				return false;
			}
			break;
		case arrayType:
		case objectType:
		default:
			return false;
			break;
		}
		return 0;
	}

	/*
		convert data to double
		default 0
	*/
	double toDouble() const
	{
		switch (type_)
		{
		case nullType:
			return 0;
			break;
		case intType:
			return (float)ref_.int_value;
			break;
		case boolType:
			return (float)ref_.bool_value;
			break;
		case doubleType:
			return (float)ref_.double_value;
			break;
		case stringType:
			{
				float tmp = 0;
				sscanf(ref_.string_value.c_str(), "%f", &tmp);
				return tmp;
			}
			break;
		case arrayType:
		case objectType:
		default:
			return 0;
			break;
		}
		return 0;
	}

	string_type toString() const
	{
		char buffer[50] = {0};
		switch (type_)
		{
		case nullType:
			return "null";
			break;
		case intType:
			{
				sprintf(buffer, "%d", ref_.int_value);
				return buffer;
			}
			break;
		case boolType:
			{
				if (ref_.bool_value)
				{
					return "true";
				}
				else
				{
					return "false";
				}
			}
			break;
		case doubleType:
			{
				sprintf(buffer, "%f", (float)ref_.double_value);
				return buffer;
			}
			break;
		case stringType:
			return ref_.string_value;
			break;
		case arrayType:
		case objectType:
		default:
			return "";
			break;
		}
		return 0;
	}


	bool operator==(const weakTypeHolder& rs)
	{
		return Equal(rs);
	}

	bool operator!=(const weakTypeHolder& rs)
	{
		return !(*this == rs);
	}

	bool operator > (const weakTypeHolder& rs)
	{
		return GreaterThan(rs);
	}

	bool operator >= (const weakTypeHolder& rs)
	{
		return GreaterEqualThan(rs);
	}

	bool operator < (const weakTypeHolder& rs)
	{
		return !(*this >= rs);
	}

	bool operator <= (const weakTypeHolder& rs)
	{
		return !(*this > rs);
	}

	bool Equal(const weakTypeHolder& rs)
	{
		switch (type_)
		{
		case nullType:
			return rs.type_ == nullType;
			break;
		case intType:
			return ref_.int_value == rs.toInt();
			break;
		case boolType:
			return ref_.bool_value == rs.toBool();
			break;
		case doubleType:
			// you know why, they are not accurly equal
			return (ref_.double_value >= rs.toDouble() - 0.001) && (ref_.double_value <= rs.toDouble() + 0.001);
			break;
		case stringType:
			return ref_.string_value == rs.toString();
			break;
		case arrayType:
			return false;
			break;
		case objectType:
			return false;
			break;
		default:
			return false;
			break;
		}
	}

	bool GreaterThan(const weakTypeHolder& rs)
	{
		switch (type_)
		{
		case nullType:
			return false;
			break;
		case intType:
			return ref_.int_value > rs.toInt();
			break;
		case boolType:
			return ref_.bool_value > rs.toBool();
			break;
		case doubleType:
			return ref_.double_value > rs.toDouble();
			break;
		case stringType:
			return ref_.string_value > rs.toString();
			break;
		case arrayType:
			return false;
			break;
		case objectType:
			return false;
			break;
		default:
			return false;
			break;
		}
	}

	bool GreaterEqualThan(const weakTypeHolder& rs)
	{
		switch (type_)
		{
		case nullType:
			return false;
			break;
		case intType:
			return ref_.int_value >= rs.toInt();
			break;
		case boolType:
			return ref_.bool_value >= rs.toBool();
			break;
		case doubleType:
			return ref_.double_value >= rs.toDouble();
			break;
		case stringType:
			return ref_.string_value >= rs.toString();
			break;
		case arrayType:
			return false;
			break;
		case objectType:
			return false;
			break;
		default:
			return false;
			break;
		}
	}

	bool In(const weakTypeHolder& rs)
	{
		if (rs.type_ != arrayType)
		{
			return false;
		}
	}

	bool InRegion(const weakTypeHolder& rs)
	{
		if (rs.type_ != arrayType)
		{
			return false;
		}
	}

	/*
		Suggest a suitable type for a string type value
		may be costly
	
		return valueType
	 */
	valueType typeSuggest()
	{	
		// nothing to do to no-string types
		if (type_ != stringType)
		{
			return type_;
		}

		if (my_stricmp(ref_.string_value.c_str(), "true") == 0)
		{
			return boolType;
		}
		if (my_stricmp(ref_.string_value.c_str(), "false") == 0)
		{
			return boolType;
		}
		if (my_stricmp(ref_.string_value.c_str(), "null") == 0)
		{
			return nullType;
		}

		bool hasDot = false;
		bool sign = true; //allow the first sign
		int c;

		for (string_type::iterator it = ref_.string_value.begin();
			it != ref_.string_value.end();
			++it)
		{
			c = (int)*it;
			if (sign && SIGN_ARRAY[c])
			{
				sign = false;
				continue;
			}

			if (DIGIT_ARRAY[c])
			{
				continue;
			}

			if (!hasDot && c == '.')
			{
				hasDot = true;
				continue;
			}

			return stringType;
		}

		if (hasDot)
		{
			return doubleType;
		}
		else
		{
			return intType;
		}
	}
	

private:
	valueType type_;
	int count_;
	char _data_[sizeof(_dataHolder)];
	_dataHolder_ref ref_;

	weakTypeHolder& operator=(const weakTypeHolder& value);
};









} //namespace _detail
} //namespace weakType
} //namespace wolv

#endif

